using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using GLWrapper;

namespace GameBaseCode
{
    internal class Particle
    {
        public Vector3 position = new Vector3();
        public Vector3 velocity = new Vector3();
        public float gravity;
        public double timeToLive;
        public float size;
        public bool canBounce;

        public static bool isDead(Particle p)
        {
            return p.timeToLive <= 0;
        }

        public void update(double dt)
        {
            timeToLive -= dt;
            velocity.y += gravity * (float)dt;
            position += velocity * (float)dt;

            if (canBounce)
            {
                if (position.y < 0)
                {
                    velocity.y *= -1;
                }
            }

        }

        public void draw()
        {
            GL.glPointSize(size);
            GL.glColor4f(1, 0, 0, (float)timeToLive / 2.0f );
            GL.glBegin(GL.GL_POINTS);
            GL.glVertex(position);
            GL.glEnd();
        }
    }

    public class ParticleManager
    {
        private static ParticleManager _instance;
        private List<Particle> liveParticles = new List<Particle>();
        private List<Particle> deadParticles = new List<Particle>();
        private Random random = new Random();

        private ParticleManager()
        {
            for (int i = 0; i < 1000; i++)
            {
                deadParticles.Add(new Particle());
            }
        }

        public static ParticleManager getInstance()
        {
            if (_instance == null)
            {
                _instance = new ParticleManager();
            }
            return _instance;
        }


        public void update(double dt)
        {
            foreach (Particle p in liveParticles)
            {
                p.update(dt);
                if (Particle.isDead(p))
                {
                    deadParticles.Add(p);
                }
            }

            liveParticles.RemoveAll( new Predicate<Particle>(Particle.isDead) );
            


        }

        public void draw()
        {
            GL.glDisable(GL.GL_TEXTURE_2D);
            GL.glEnable(GL.GL_BLEND);
            GL.glBlendFunc(GL.GL_SRC_ALPHA, GL.GL_ONE_MINUS_SRC_ALPHA);
            GL.glEnable(GL.GL_POINT_SMOOTH);
            
            foreach (Particle p in liveParticles)
            {
                p.draw();
            }
            GL.glPointSize(1);
            GL.glDisable(GL.GL_POINT_SMOOTH);
            GL.glDisable(GL.GL_BLEND);
        }

        public void createSplatter(Vector3 center)
        {
            int prefNumNeeded = 100;
            for (int i = 0; i < prefNumNeeded; i++)
            {
                if (deadParticles.Count <= 0)
                {
                    return;
                }
                else
                {
                    Particle p = deadParticles[0];
                    deadParticles.RemoveAt(0);
                    p.position = center;
                    p.position.y += 5;
                    // velocity [-1,1]
                    p.velocity.x = (float)random.NextDouble() * 2 - 1;
                    p.velocity.y = (float)random.NextDouble() * 2 - 1; 
                    p.velocity.z = (float)random.NextDouble() * 2 - 1;
                    p.velocity.normalize(); // sphere
                    p.velocity.y += 1.5f;
                    p.velocity *= 5;
                    p.gravity = -50;
                    p.timeToLive = 2;
                    p.size = 10;
                    p.canBounce = true;
                    liveParticles.Add(p);
                }
            }
        }


        public void createHitSplatter(Vector3 spawnPos, Vector3 direction)
        {
            int prefNumNeeded = 50;
            for (int i = 0; i < prefNumNeeded; i++)
            {
                if (deadParticles.Count <= 0)
                {
                    return;
                }
                else
                {
                    Particle p = deadParticles[0];
                    deadParticles.RemoveAt(0);
                    p.position = spawnPos;
                    p.velocity = -direction;
                    p.velocity.normalize();
                    p.velocity.randomize(65);
                    p.velocity *= 10;

                    p.gravity = -50;
                    p.timeToLive = 2;
                    p.size = 5;
                    p.canBounce = false;
                    liveParticles.Add(p);
                }
            }
        }
    }
}